#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>

#define SERVER_PORT 7000

int sock;

typedef struct {
    char ip[16];   // 发送端的IP地址
    uint16_t port;   // 发送端的端口号
    char cmd[256];  // 接收到命令数据
    ssize_t packetlen; // 数据包长度

    struct sockaddr_in cli_addr;
    socklen_t cli_addr_len;

} Client;

void *cli_task(void *arg){  // 接收到客户端指令后，在线程任务中处理
    Client *cli = (Client *)arg;
    printf("%s(%d): %s\n", cli->ip, cli->port, cli->cmd);

    // 执行cmd指令
    char cmd[512]="";
    sprintf(cmd, "%s > a.txt", cli->cmd);

    system(cmd);
    sleep(2);

    // 读取命令执行结果
    struct stat s;
    int ret=stat("a.txt", &s);
    if(ret==0){ // 读取文件信息成功
        char *retbuf = (char *)malloc(s.st_size);
        int fd = open("a.txt",O_RDONLY);
        ssize_t datalen = read(fd, retbuf, s.st_size);

        // 响应指令执行结果
        sendto(sock, retbuf, datalen, 0, (struct sockaddr *)&cli->cli_addr, cli->cli_addr_len);

        close(fd);
        free(retbuf);

    }else{
        // 执行指令失败
        sendto(sock, "fail", 4, 0, (struct sockaddr *)&cli->cli_addr, cli->cli_addr_len);
    }

    free(cli);
    return NULL;
}

int main(int argc, char const *argv[])
{
    sock=socket(AF_INET, SOCK_DGRAM, 0);

    struct sockaddr_in addr = {
        .sin_family = AF_INET,
        .sin_port = htons(SERVER_PORT),
        .sin_addr.s_addr = INADDR_ANY
    };

    if(bind(sock, (struct sockaddr *)&addr, sizeof(addr)) != 0){
        perror("bind");
        return -1;
    }

    // 开始接收数据
    printf("---linux远程命令执行服务已开启---\n");

    while(1){

        Client *cli = (Client *)calloc(1, sizeof(Client)); 
        cli->cli_addr_len = sizeof(cli->cli_addr);
        // 接收客户端发送过来的数据包（最后两个参数表示 客户端的地址信息：由系统填充）
        cli->packetlen = recvfrom(sock, cli->cmd, 256, 0, (struct sockaddr *)&cli->cli_addr, &cli->cli_addr_len);

        inet_ntop(AF_INET, &cli->cli_addr.sin_addr.s_addr, cli->ip, 16);  // 提取客户端的IP地址
        cli->port =  ntohs(cli->cli_addr.sin_port);   // 提取客户端的PORT端口号

        pthread_t tid;
        pthread_create(&tid, NULL, cli_task, cli);

        sleep(1);
    }

    return 0;
}
